#include "chainhash.h"
#include <iostream>

using namespace hashtable;

ListElmt::ListElmt(int key, int data)
{
    this->m_key = key;
    this->m_data = data;
    this->m_pre = NULL;
    this->m_next = NULL;
}

void ListElmt::SetKey(int k)
{
    this->m_key = k;
}

void ListElmt::SetData(int d)
{
    this->m_data = d;
}

void ListElmt::SetPre(ListElmt* p)
{
    this->m_pre = p;
}

void ListElmt::SetNext(ListElmt* n)
{
    this->m_next = n;
}

int ListElmt::GetKey()
{
    return this->m_key;
}

int ListElmt::GetData()
{
    return this->m_data;
}

ListElmt* ListElmt::GetNext()
{
    return this->m_next;
}

ListElmt* ListElmt::GetPre()
{
    return this->m_pre;
}

DoubleList::DoubleList()
{
      this->m_head = NULL;
      this->m_tail = NULL;
      this->m_size = 0;
}

DoubleList::~DoubleList()
{
    if (this->m_head != NULL)
    {
        ListElmt* cur = NULL;
        while (this->m_head)
        {
           cur = this->m_head;
           this->m_head = this->m_head->GetNext();
            delete cur;
        }
    }
}

ListElmt* DoubleList::ListHead()
{
    return this->m_head;
}

ListElmt* DoubleList::ListTail()
{
    return this->m_tail;
}

int DoubleList::GetListSize()
{
    return this->m_size;
}

ListElmt* DoubleList::ListDel(ListElmt* e)
{
    if (e == NULL || this->m_size == 0)
    {
        return NULL;
    }

    if (e == this->m_head)
    {
        this->m_head = this->m_head->GetNext();
        if (this->m_head == NULL)
        {
            this->m_tail = NULL;
        }
        else
        {
            this->m_head->SetPre(NULL);
        }
    }
    else
    {
        e->GetPre()->SetNext(e->GetNext());
        if (e->GetNext() != NULL)
        {
            e->GetNext()->SetPre(e->GetPre());
        }
        else
        {
            this->m_tail = e->GetPre();
        }
    }

    --this->m_size;
    return e;
}

void DoubleList::ListInsert(ListElmt* e)
{
    if (e == NULL)
    {
        return;
    }

    ++this->m_size;
    if (this->m_head == NULL)
    {
        this->m_head = e;
        this->m_tail = e;
        return;
    }

    this->m_tail->SetNext(e);
    e->SetPre(this->m_tail);
    this->m_tail = e;
}

void DoubleList::ListInsert(ListElmt* e, ListElmt* i)
{
     if (e == NULL)
     {
         return;
     }

     ListElmt* next = e->GetNext();
     e->SetNext(i);
     i->SetPre(e);
     i->SetNext(next);
     if (next != NULL)
     {
         next->SetPre(i);
     }
     ++this->m_size;
}

/////////////////////////////////////////////////////////

ChainHashTable::ChainHashTable(int b)
{
    this->m_buckets = b;
    this->m_data = new DoubleList[b];
    this->m_size = 0;
}

int ChainHashTable::GetHashSize()
{
    return this->m_size;
}

void ChainHashTable::ChtblInsert(int k, int d)
{
    int t = 0;
    if (this->ChtblLookup(k, t))
    {
        return;
    }

    int idx = this->hashbuckets(k);
    this->m_data[idx].ListInsert(new ListElmt(k, d));
    ++this->m_size;
}

void ChainHashTable::ChtblDel(int k)
{
     int idx = this->hashbuckets(k);
     ListElmt* head = this->m_data[idx].ListHead();
     while(head)
     {
         if (head->GetKey() == k)
         {
             this->m_data[idx].ListDel(head);
             --this->m_size;
             return;
         }
         head = head->GetNext();
     }
}

bool ChainHashTable::ChtblLookup(int k, int& d)
{
    int idx = this->hashbuckets(k);
    ListElmt* head = this->m_data[idx].ListHead();
    while(head)
    {
        if (head->GetKey() == k)
        {
            d = head->GetData();
            return true;
        }
        head = head->GetNext();
    }

    return false;
}

int ChainHashTable::hashbuckets(int k)
{
    return k%this->m_buckets;
}
